home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / osr5 / sco / scripts / admin / hdminor < prev    next >
Encoding:
AWK Script  |  1997-08-26  |  37.5 KB  |  1,057 lines

  1. #!/usr/local/bin/gawk -f
  2. # hdminor: show meaning of hard drive device minor bits
  3. # @(#) hdminor.gawk 2.0 97/06/10
  4. # 90/12/28 john h. dubois iii
  5. # 91/02/25 added comments
  6. # 91/04/29 added help
  7. # 92/09/29 added devicename args, multiple command line args
  8. # 97/03/10 Allow extended minor numbers; deal with extended DOS partitions.
  9. #          Still doesn't recognize extended minor #s when device name given.
  10. # 97/05/22 Added mt options.
  11. # 97/06/10 Was ksh script; rewrote in awk; added alt char graphics
  12.  
  13. BEGIN {
  14.     Name = "hdminor"
  15.     Usage = "Usage: "Name " [-hMtT] <minor_num>|<device-name> ..."
  16.     ARGC = Opts(Name,Usage,"hMtTx",1)
  17.     Debug = "x" in Options
  18.  
  19.     if ("h" in Options) {
  20.     printf \
  21. "%s: show meaning of hard drive minor numbers or device node names.\n"\
  22. "%s\n"\
  23. "If a minor number is given, the meaning of the physical drive, partition,\n"\
  24. "and division bit fields of the minor number will be printed.  Numbers may\n"\
  25. "be given in ksh syntax (radix#value).  If a device name is given, its\n"\
  26. "minor number is looked up and the fields are printed as above.\n"\
  27. "Options:\n"\
  28. "-h: Print this help.\n"\
  29. "-t: Verbose operation; show how the minor numbers are decomposed/converted.\n"\
  30. "-T: Like -t, but without using the display's alternate character set.\n"\
  31. "-M: Print an explanation of hd minor number meaning.\n",
  32.     Name,Usage
  33.     exit 0
  34.     }
  35.     if ("M" in Options) {
  36.     print \
  37. "Minor number meaning:\n"\
  38. "Bits    Meaning\n"\
  39. "76    Physical drive (0, 1, 2 or 3)\n"\
  40. "543    Virtual drive (partition)\n"\
  41. "    0    whole physical drive\n"\
  42. "        (divvy partition bits ignored; should be 000)\n"\
  43. "    1,2,3,4    virtual drive 1, 2, 3, or 4\n"\
  44. "    5    active virtual drive\n"\
  45. "    6    DOS virtual drive\n"\
  46. "        Divvy partition bits should be 111 (whole virtual drive)\n"\
  47. "    7    not used\n"\
  48. "210    Divvy partition (division) or DOS drive\n"\
  49. "    For a divvied (UNIX) partition:\n"\
  50. "    0-6    Divvy partition 0-6\n"\
  51. "    7        whole virtual drive\n"\
  52. "    For a DOS partition:\n"\
  53. "    0    Primary DOS partition; logical drive C:\n"\
  54. "    1-7    Extended DOS partitions; logical drives D:-J:"
  55.     exit 0
  56.     }
  57.     Verbose = "t" in Options || "T" in Options
  58.     noAlt = "T" in Options
  59.     if (Verbose && !noAlt)
  60.     noAlt = altInit(tinfo,"",1,AltMap)
  61.  
  62.     hdminor(ARGV[1])
  63.     for (i = 2; i < ARGC; i++) {
  64.     print ""
  65.     hdminor(ARGV[i])
  66.     }
  67. }
  68.  
  69. function hdminor(s,
  70. minor,device,shprog,elem,Cmd,lout,pd,vd,division,v_pd,v_vd,v_div,Format) {
  71.     if (s ~ /^([0-9]+|[1-9][0-9]*#[0-9a-zA-Z]+)$/) {
  72.     minor = kshbase(s)
  73.     if (minor > 65535) {
  74.         print "Minor number must be in the range 0-65535!" > "/dev/stderr"
  75.         return 0
  76.     }
  77.     }
  78.     else if (s ~ "/")
  79.     device = s
  80.     else if (s ~ /[ \t]/)
  81.     printf "Bad device name: %s\n",s > "/dev/stderr"
  82.     else {
  83.     shprog = sprintf("for d in %s dsk/%s rdsk/%s; do\nd=/dev/$d;"\
  84.     "[ -c $d -o -b $d ] && { echo $d; exit 0; }; done",s,s,s)
  85.     if (Debug)
  86.         print "find command: " shprog > "/dev/stderr"
  87.     shprog | getline device
  88.     close(shprog)
  89.     if (device == "") {
  90.         printf "%s: %s: no such character or block device.\n",Name,s \
  91.         > "/dev/stderr"
  92.         return 0
  93.     }
  94.     }
  95.  
  96.     if (device != "") {
  97.     Cmd = "exec ls -lon -- " device
  98.     if (Debug)
  99.         print "stat command: " Cmd > "/dev/stderr"
  100.     Cmd | getline lout
  101.     if (Debug)
  102.         print "stat output:\n" lout > "/dev/stderr"
  103.     close(Cmd)
  104.     if (split(lout,elem,"[ ,]+") < 5)
  105.         return 0    # presumably ls emitted an error message
  106.     if (elem[1] !~ /^[bc]/) {
  107.         printf "%s: Error: not a block or character device: %s",
  108.         Name,device > "/dev/stderr"
  109.         return 0
  110.     }
  111.     minor = elem[5]
  112.     printf "Device %s (minor number %s):\n",device,minor
  113.     }
  114.     else
  115.     printf "Minor number %s:\n",minor
  116.  
  117.     # divide up the bits
  118.     pd = int(minor/64)
  119.     vd = int(minor/8) % 8
  120.     division = minor % 8
  121.  
  122.     v_pd = "Physical drive: " pd
  123.  
  124.     if (vd == 0) {
  125.     v_vd="0 (none; whole physical drive)"
  126.     if (division)
  127.         print \
  128. "Divvy partition should be 0 when virtual drive is 0 (whole physical drive)!"
  129.     }
  130.     else if (vd <= 4)
  131.     v_vd = vd
  132.     else if (vd == 5)
  133.     v_vd="5 (active)"
  134.     else if (vd == 6)
  135.     v_vd = "6 (DOS)"
  136.     else if (vd == 7)
  137.     v_vd = "7 - invalid virtual drive!"
  138.     v_vd = "Virtual drive (partition): " v_vd
  139.  
  140.     if (vd == 6) {
  141.     if (division == 0)
  142.         v_div = "C: (primary DOS partition)"
  143.     else
  144.         v_div = sprintf("%c (extended DOS partition)",67+division)
  145.     v_div = "DOS logical drive: " v_div
  146.     }
  147.     else {
  148.     if (division == 7)
  149.         v_div = "7 (whole division)"
  150.     else
  151.         v_div = division
  152.     esac
  153.     v_div = "Division (divvy partition): " v_div
  154.     }
  155.  
  156.     if (Verbose) {
  157.     printf \
  158. "%d = binary %s, octal %03o\n"\
  159. "%5s\n",minor,itoa(minor,2,8),minor,itoa(minor,8,3)
  160.     doLine("|||","")
  161.     doLine("||\\-->",v_div)
  162.     doLine("|\\--->",v_vd)
  163.     doLine("\\---->",v_pd)
  164.     }
  165.     else
  166.     printf \
  167. "%s\n"\
  168. "%s\n"\
  169. "%s\n",v_pd,v_vd,v_div
  170. }
  171.  
  172. function doLine(l,val,  len,i,c) {
  173.     printf "  %s",_macs[1]
  174.     if (noAlt)
  175.     printf "%s",l
  176.     else {
  177.     len = length(l)
  178.     for (i = 1; i <= len; i++) {
  179.         c = substr(l,i,1)
  180.         if (c == "|")
  181.         printf "%s",AltMap["x"]
  182.         else if (c == "\\")
  183.         printf "%s",AltMap["m"]
  184.         else if (c == "-")
  185.         printf "%s",AltMap["q"]
  186.         else if (c == ">")
  187.         printf "%s",AltMap["+"]
  188.     }
  189.     }
  190.     printf "%s%s\n",_macs[0],val
  191. }
  192.  
  193. ### Start of ProcArgs library
  194. # @(#) ProcArgs 1.9 96/10/15
  195. # 92/02/29 john h. dubois iii (john@armory.com)
  196. # 93/07/18 Added "#" arg type
  197. # 93/09/26 Do not count -h against MinArgs
  198. # 94/01/01 Stop scanning at first non-option arg.  Added ">" option type.
  199. #          Removed meaning of "+" or "-" by itself.
  200. # 94/03/08 Added & option and *()< option types.
  201. # 94/04/02 Added NoRCopt to Opts()
  202. # 94/06/11 Mark numeric variables as such.
  203. # 94/07/08 Opts(): Do not require any args if h option is given.
  204. # 94/09/23 Fixed bug that caused fail if -opt<value> given as last arg.
  205. # 95/01/22 Record options given more than once.  Record option num in argv.
  206. # 95/06/08 Added ExclusiveOptions().
  207. # 96/01/20 Let rcfiles be a colon-separated list of filenames.
  208. #          Expand $VARNAME at the start of its filenames.
  209. #          Let varname=0 and -option- turn off an option.
  210. # 96/05/05 Changed meaning of 7th arg to Opts; now can specify exactly how many
  211. #          of the vars should be searched for in the environment.
  212. #          Check for duplicate rcfiles.
  213. # 96/05/13 Return more specific error values.  Note: ProcArgs() and InitOpts()
  214. #          now return various negatives values on error, not just -1, and
  215. #          Opts() may set Err to various positive values, not just 1.
  216. #          Added AllowUnrecOpt.
  217. # 96/05/23 Check type given for & option
  218. # 96/06/15 Re-port to awk
  219. # 96/10/01 Moved file-reading code into ReadConfFile(), so that it can be
  220. #          used by other functions.
  221. # 96/10/15 Added OptChars
  222.  
  223. # optlist is a string which contains all of the possible command line options.
  224. # A character followed by certain characters indicates that the option takes
  225. # an argument, with type as follows:
  226. # :    String argument
  227. # *    Floating point argument
  228. # (    Non-negative floating point argument
  229. # )    Positive floating point argument
  230. # #    Integer argument
  231. # <    Non-negative integer argument
  232. # >    Positive integer argument
  233. # The only difference the type of argument makes is in the runtime argument
  234. # error checking that is done.
  235.  
  236. # The & option is a special case used to get numeric options without the
  237. # user having to give an option character.  It is shorthand for [-+.0-9].
  238. # If & is included in optlist and an option string that begins with one of
  239. # these characters is seen, the value given to "&" will include the first
  240. # char of the option.  & must be followed by a type character other than ":".
  241. # Note that if e.g. &> is given, an option of -.5 will produce an error.
  242.  
  243. # Strings in argv[] which begin with "-" or "+" are taken to be
  244. # strings of options, except that a string which consists solely of "-"
  245. # or "+" is taken to be a non-option string; like other non-option strings,
  246. # it stops the scanning of argv and is left in argv[].
  247. # An argument of "--" or "++" also stops the scanning of argv[] but is removed.
  248. # If an option takes an argument, the argument may either immediately
  249. # follow it or be given separately.
  250. # "-" and "+" options are treated the same.  "+" is allowed because most awks
  251. # take any -options to be arguments to themselves.  gawk 2.15 was enhanced to
  252. # stop scanning when it encounters an unrecognized option, though until 2.15.5
  253. # this feature had a bug that caused problems in some cases.  See the OptChars
  254. # parameter to explicitely set the option-specifier characters.
  255.  
  256. # If an option that does not take an argument is given,
  257. # an index with its name is created in Options and its value is set to the
  258. # number of times it occurs in argv[].
  259.  
  260. # If an option that does take an argument is given, an index with its name is
  261. # created in Options and its value is set to the value of the argument given
  262. # for it, and Options[option-name,"count"] is (initially) set to the 1.
  263. # If an option that takes an argument is given more than once,
  264. # Options[option-name,"count"] is incremented, and the value is assigned to
  265. # the index (option-name,instance) where instance is 2 for the second occurance
  266. # of the option, etc.
  267. # In other words, the first time an option with a value is encountered, the
  268. # value is assigned to an index consisting only of its name; for any further
  269. # occurances of the option, the value index has an extra (count) dimension.
  270.  
  271. # The sequence number for each option found in argv[] is stored in
  272. # Options[option-name,"num",instance], where instance is 1 for the first
  273. # occurance of the option, etc.  The sequence number starts at 1 and is
  274. # incremented for each option, both those that have a value and those that
  275. # do not.  Options set from a config file have a value of 0 assigned to this.
  276.  
  277. # Options and their arguments are deleted from argv.
  278. # Note that this means that there may be gaps left in the indices of argv[].
  279. # If compress is nonzero, argv[] is packed by moving its elements so that
  280. # they have contiguous integer indices starting with 0.
  281. # Option processing will stop with the first unrecognized option, just as
  282. # though -- was given except that unlike -- the unrecognized option will not be
  283. # removed from ARGV[].  Normally, an error value is returned in this case.
  284. # If AllowUnrecOpt is true, it is not an error for an unrecognized option to
  285. # be found, so the number of remaining arguments is returned instead.
  286. # If OptChars is not a null string, it is the set of characters that indicate
  287. # that an argument is an option string if the string begins with one of the
  288. # characters.  A string consisting solely of two of the same option-indicator
  289. # characters stops the scanning of argv[].  The default is "-+".
  290. # argv[0] is not examined.
  291. # The number of arguments left in argc is returned.
  292. # If an error occurs, the global string OptErr is set to an error message
  293. # and a negative value is returned.
  294. # Current error values:
  295. # -1: option that required an argument did not get it.
  296. # -2: argument of incorrect type supplied for an option.
  297. # -3: unrecognized (invalid) option.
  298. function ProcArgs(argc,argv,OptList,Options,compress,AllowUnrecOpt,OptChars,
  299. ArgNum,ArgsLeft,Arg,ArgLen,ArgInd,Option,Pos,NumOpt,Value,HadValue,specGiven,
  300. NeedNextOpt,GotValue,OptionNum,Escape,dest,src,count,c,OptTerm,OptCharSet)
  301. {
  302. # ArgNum is the index of the argument being processed.
  303. # ArgsLeft is the number of arguments left in argv.
  304. # Arg is the argument being processed.
  305. # ArgLen is the length of the argument being processed.
  306. # ArgInd is the position of the character in Arg being processed.
  307. # Option is the character in Arg being processed.
  308. # Pos is the position in OptList of the option being processed.
  309. # NumOpt is true if a numeric option may be given.
  310.     ArgsLeft = argc
  311.     NumOpt = index(OptList,"&")
  312.     OptionNum = 0
  313.     if (OptChars == "")
  314.     OptChars = "-+"
  315.     while (OptChars != "") {
  316.     c = substr(OptChars,1,1)
  317.     OptChars = substr(OptChars,2)
  318.     OptCharSet[c]
  319.     OptTerm[c c]
  320.     }
  321.     for (ArgNum = 1; ArgNum < argc; ArgNum++) {
  322.     Arg = argv[ArgNum]
  323.     if (length(Arg) < 2 || !((specGiven = substr(Arg,1,1)) in OptCharSet))
  324.         break    # Not an option; quit
  325.     if (Arg in OptTerm) {
  326.         delete argv[ArgNum]
  327.         ArgsLeft--
  328.         break
  329.     }
  330.     ArgLen = length(Arg)
  331.     for (ArgInd = 2; ArgInd <= ArgLen; ArgInd++) {
  332.         Option = substr(Arg,ArgInd,1)
  333.         if (NumOpt && Option ~ /[-+.0-9]/) {
  334.         # If this option is a numeric option, make its flag be & and
  335.         # its option string flag position be the position of & in
  336.         # the option string.
  337.         Option = "&"
  338.         Pos = NumOpt
  339.         # Prefix Arg with a char so that ArgInd will point to the
  340.         # first char of the numeric option.
  341.         Arg = "&" Arg
  342.         ArgLen++
  343.         }
  344.         # Find position of flag in option string, to get its type (if any).
  345.         # Disallow & as literal flag.
  346.         else if (!(Pos = index(OptList,Option)) || Option == "&") {
  347.         if (AllowUnrecOpt) {
  348.             Escape = 1
  349.             break
  350.         }
  351.         else {
  352.             OptErr = "Invalid option: " specGiven Option
  353.             return -3
  354.         }
  355.         }
  356.  
  357.         # Find what the value of the option will be if it takes one.
  358.         # NeedNextOpt is true if the option specifier is the last char of
  359.         # this arg, which means that if the option requires a value it is
  360.         # the next arg.
  361.         if (NeedNextOpt = (ArgInd >= ArgLen)) { # Value is the next arg
  362.         if (GotValue = ArgNum + 1 < argc)
  363.             Value = argv[ArgNum+1]
  364.         }
  365.         else {    # Value is included with option
  366.         Value = substr(Arg,ArgInd + 1)
  367.         GotValue = 1
  368.         }
  369.  
  370.         if (HadValue = AssignVal(Option,Value,Options,
  371.         substr(OptList,Pos + 1,1),GotValue,"",++OptionNum,!NeedNextOpt,
  372.         specGiven)) {
  373.         if (HadValue < 0)    # error occured
  374.             return HadValue
  375.         if (HadValue == 2)
  376.             ArgInd++    # Account for the single-char value we used.
  377.         else {
  378.             if (NeedNextOpt) {    # option took next arg as value
  379.             delete argv[++ArgNum]
  380.             ArgsLeft--
  381.             }
  382.             break    # This option has been used up
  383.         }
  384.         }
  385.     }
  386.     if (Escape)
  387.         break
  388.     # Do not delete arg until after processing of it, so that if it is not
  389.     # recognized it can be left in ARGV[].
  390.     delete argv[ArgNum]
  391.     ArgsLeft--
  392.     }
  393.     if (compress != 0) {
  394.     dest = 1
  395.     src = argc - ArgsLeft + 1
  396.     for (count = ArgsLeft - 1; count; count--) {
  397.         ARGV[dest] = ARGV[src]
  398.         dest++
  399.         src++
  400.     }
  401.     }
  402.     return ArgsLeft
  403. }
  404.  
  405. # Assignment to values in Options[] occurs only in this function.
  406. # Option: Option specifier character.
  407. # Value: Value to be assigned to option, if it takes a value.
  408. # Options[]: Options array to return values in.
  409. # ArgType: Argument type specifier character.
  410. # GotValue: Whether any value is available to be assigned to this option.
  411. # Name: Name of option being processed.
  412. # OptionNum: Number of this option (starting with 1) if set in argv[],
  413. #     or 0 if it was given in a config file or in the environment.
  414. # SingleOpt: true if the value (if any) that is available for this option was
  415. #     given as part of the same command line arg as the option.  Used only for
  416. #     options from the command line.
  417. # specGiven is the option specifier character use, if any (e.g. - or +),
  418. # for use in error messages.
  419. # Global variables: OptErr
  420. # Return value: negative value on error, 0 if option did not require an
  421. # argument, 1 if it did & used the whole arg, 2 if it required just one char of
  422. # the arg.
  423. # Current error values:
  424. # -1: Option that required an argument did not get it.
  425. # -2: Value of incorrect type supplied for option.
  426. # -3: Bad type given for option &
  427. function AssignVal(Option,Value,Options,ArgType,GotValue,Name,OptionNum,
  428. SingleOpt,specGiven,  UsedValue,Err,NumTypes) {
  429.     # If option takes a value...
  430. #    printf "option=<%s> value=<%s>\n",Option,Value
  431.     NumTypes = "*()#<>]"
  432.     if (Option == "&" && ArgType !~ "[" NumTypes) {
  433.     OptErr = "Bad type given for & option"
  434.     return -3
  435.     }
  436.  
  437.     if (UsedValue = (ArgType ~ "[:" NumTypes)) {
  438.     if (!GotValue) {
  439.         if (Name != "")
  440.         OptErr = "Variable requires a value -- " Name
  441.         else
  442.         OptErr = "option requires an argument -- " Option
  443.         return -1
  444.     }
  445.     if ((Err = CheckType(ArgType,Value,Option,Name,specGiven)) != "") {
  446.         OptErr = Err
  447.         return -2
  448.     }
  449.     # Mark this as a numeric variable; will be propogated to Options[] val.
  450.     if (ArgType != ":")
  451.         Value += 0
  452.     if ((Instance = ++Options[Option,"count"]) > 1)
  453.         Options[Option,Instance] = Value
  454.     else
  455.         Options[Option] = Value
  456.     }
  457.     # If this is an environ or rcfile assignment & it was given a value...
  458.     else if (!OptionNum && Value != "") {
  459.     UsedValue = 1
  460.     # If the value is "0" or "-" and this is the first instance of it,
  461.     # do not set Options[Option]; this allows an assignment in an rcfile to
  462.     # turn off an option (for the simple "Option in Options" test) in such
  463.     # a way that it cannot be turned on in a later file.
  464.     if (!(Option in Options) && (Value == "0" || Value == "-"))
  465.         Instance = 1
  466.     else
  467.         Instance = ++Options[Option]
  468.     # Save the value even though this is a flag
  469.     Options[Option,Instance] = Value
  470.     }
  471.     # If this is a command line flag and has a - following it in the same arg,
  472.     # it is being turned off.
  473.     else if (OptionNum && SingleOpt && substr(Value,1,1) == "-") {
  474.     UsedValue = 2
  475.     if (Option in Options)
  476.         Instance = ++Options[Option]
  477.     else
  478.         Instance = 1
  479.     Options[Option,Instance]
  480.     }
  481.     # If this is a flag assignment without a value, increment the count for the
  482.     # flag unless it was turned off.  The indicator for a flag being turned off
  483.     # is that the flag index has not been set in Options[] but it has an
  484.     # instance count.
  485.     else if (Option in Options || !((Option,1) in Options))
  486.     # Increment number of times this flag seen; will inc null value to 1
  487.     Instance = ++Options[Option]
  488.     Options[Option,"num",Instance] = OptionNum
  489.     return UsedValue
  490. }
  491.  
  492. # Option is the option letter
  493. # Value is the value being assigned
  494. # Name is the var name of the option, if any
  495. # ArgType is one of:
  496. # :    String argument
  497. # *    Floating point argument
  498. # (    Non-negative floating point argument
  499. # )    Positive floating point argument
  500. # #    Integer argument
  501. # <    Non-negative integer argument
  502. # >    Positive integer argument
  503. # specGiven is the option specifier character use, if any (e.g. - or +),
  504. # for use in error messages.
  505. # Returns null on success, err string on error
  506. function CheckType(ArgType,Value,Option,Name,specGiven,  Err,ErrStr) {
  507.     if (ArgType == ":")
  508.     return ""
  509.     # A number begins with optional + or -, and is followed by a string of
  510.     # digits or a decimal with digits before it, after it, or both
  511.     if (Value !~ /^[-+]?([0-9]+|[0-9]*\.[0-9]+|[0-9]+\.)$/)
  512.     Err = "must be a number"
  513.     else if (ArgType ~ "[#<>]" && Value ~ /\./)
  514.     Err = "may not include a fraction"
  515.     else if (ArgType ~ "[()<>]" && Value < 0)
  516.     Err = "may not be negative"
  517.     else if (ArgType ~ "[)>]" && Value == 0)
  518.     Err = "must be a positive number"
  519.     if (Err != "") {
  520.     ErrStr = "Bad value \"" Value "\".  Value assigned to "
  521.     if (Name != "")
  522.         return ErrStr "variable " substr(Name,1,1) " " Err
  523.     else {
  524.         if (Option == "&")
  525.         Option = Value
  526.         return ErrStr "option " specGiven substr(Option,1,1) " " Err
  527.     }
  528.     }
  529.     else
  530.     return ""
  531. }
  532.  
  533. # Note: only the above functions are needed by ProcArgs.
  534. # The rest of these functions call ProcArgs() and also do other
  535. # option-processing stuff.
  536.  
  537. # Opts: Process command line arguments.
  538. # Opts processes command line arguments using ProcArgs()
  539. # and checks for errors.  If an error occurs, a message is printed
  540. # and the program is exited.
  541. #
  542. # Input variables:
  543. # Name is the name of the program, for error messages.
  544. # Usage is a usage message, for error messages.
  545. # OptList the option description string, as used by ProcArgs().
  546. # MinArgs is the minimum number of non-option arguments that this
  547. # program should have, non including ARGV[0] and +h.
  548. # If the program does not require any non-option arguments,
  549. # MinArgs should be omitted or given as 0.
  550. # rcFiles, if given, is a colon-seprated list of filenames to read for
  551. # variable initialization.  If a filename begins with ~/, the ~ is replaced
  552. # by the value of the environment variable HOME.  If a filename begins with
  553. # $, the part from the character after the $ up until (but not including)
  554. # the first character not in [a-zA-Z0-9_] will be searched for in the
  555. # environment; if found its value will be substituted, if not the filename will
  556. # be discarded.
  557. # rcfiles are read in the order given.
  558. # Values given in them will not override values given on the command line,
  559. # and values given in later files will not override those set in earlier
  560. # files, because AssignVal() will store each with a different instance index.
  561. # The first instance of each variable, either on the command line or in an
  562. # rcfile, will be stored with no instance index, and this is the value
  563. # normally used by programs that call this function.
  564. # VarNames is a comma-separated list of variable names to map to options,
  565. # in the same order as the options are given in OptList.
  566. # If EnvSearch is given and nonzero, the first EnvSearch variables will also be
  567. # searched for in the environment.  If set to -1, all values will be searched
  568. # for in the environment.  Values given in the environment will override
  569. # those given in the rcfiles but not those given on the command line.
  570. # NoRCopt, if given, is an additional letter option that if given on the
  571. # command line prevents the rcfiles from being read.
  572. # Special options:
  573. # If x is made an option and is given, some debugging info is output.
  574. # h is assumed to be the help option.
  575.  
  576. # Global variables:
  577. # The command line arguments are taken from ARGV[].
  578. # The arguments that are option specifiers and values are removed from
  579. # ARGV[], leaving only ARGV[0] and the non-option arguments.
  580. # The number of elements in ARGV[] should be in ARGC.
  581. # After processing, ARGC is set to the number of elements left in ARGV[].
  582. # The option values are put in Options[].
  583. # On error, Err is set to a positive integer value so it can be checked for in
  584. # an END block.
  585.  
  586. # Return value: The number of elements left in ARGV is returned.
  587.  
  588. function Opts(Name,Usage,OptList,MinArgs,rcFiles,VarNames,EnvSearch,NoRCopt,
  589. AllowUnrecOpt,optChars,  ArgsLeft,e) {
  590.     if (MinArgs == "")
  591.     MinArgs = 0
  592.     ArgsLeft = ProcArgs(ARGC,ARGV,OptList NoRCopt,Options,1,AllowUnrecOpt,
  593.     optChars)
  594. #    if ((ArgsLeft + ("h" in Options)) < (MinArgs+1)) {
  595.     if (ArgsLeft < (MinArgs+1) && !("h" in Options)) {
  596.     if (ArgsLeft >= 0) {
  597.         OptErr = "Not enough arguments"
  598.         Err = 4
  599.     }
  600.     else
  601.         Err = -ArgsLeft
  602.     print Name ": " OptErr ".\nUse -h for help."
  603.     print Usage
  604.     exit 1
  605.     }
  606.     if (rcFiles != "" && (NoRCopt == "" || !(NoRCopt in Options)) &&
  607.     (e = InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch)) < 0)
  608.     {
  609.     print Name ": " OptErr ".\nUse -h for help."
  610.     Err = -e
  611.     exit 1
  612.     }
  613.     return ArgsLeft
  614. }
  615.  
  616. # ReadConfFile(): Read a file containing var/value assignments, in the form
  617. # <variable-name><assignment-char><value>.
  618. # Whitespace (spaces and tabs) around a variable (leading whitespace on the
  619. # line and whitespace between the variable name and the assignment character) 
  620. # is stripped.  Lines that do not contain an assignment operator or which
  621. # contain a null variable name are ignored, other than possibly being noted in
  622. # the return value.  If more than one assignment is made to a variable, the
  623. # first assignment is used.
  624. # Input variables:
  625. # File is the file to read.
  626. # Comment is the line-comment character.  If it is found as the first non-
  627. #     whitespace character on a line, the line is ignored.
  628. # Assign is the assignment string.  The first instance of Assign on a line
  629. #     separates the variable name from its value.
  630. # If StripWhite is true, whitespace around the value (whitespace between the
  631. #     assignment char and trailing whitespace on the line) is stripped.
  632. # VarPat is a pattern that variable names must match.  
  633. #     Example: "^[a-zA-Z][a-zA-Z0-9]+$"
  634. # If FlagsOK is true, variables are allowed to be "set" by being put alone on
  635. #     a line; no assignment operator is needed.  These variables are set in
  636. #     the output array with a null value.  Lines containing nothing but
  637. #     whitespace are still ignored.
  638. # Output variables:
  639. # Values[] contains the assignments, with the indexes being the variable names
  640. #     and the values being the assigned values.
  641. # Lines[] contains the line number that each variable occured on.  A flag set
  642. #     is record by giving it an index in Lines[] but not in Values[].
  643. # Return value:
  644. # If any errors occur, a string consisting of descriptions of the errors
  645. # separated by newlines is returned.  In no case will the string start with a
  646. # numeric value.  If no errors occur,  the number of lines read is returned.
  647. function ReadConfigFile(Values,Lines,File,Comment,Assign,StripWhite,VarPat,
  648. FlagsOK,
  649. Line,Status,Errs,AssignLen,LineNum,Var,Val) {
  650.     if (Comment != "")
  651.     Comment = "^" Comment
  652.     AssignLen = length(Assign)
  653.     if (VarPat == "")
  654.     VarPat = "."    # null varname not allowed
  655.     while ((Status = (getline Line < File)) == 1) {
  656.     LineNum++
  657.     sub("^[ \t]+","",Line)
  658.     if (Line == "")        # blank line
  659.         continue
  660.     if (Comment != "" && Line ~ Comment)
  661.         continue
  662.     if (Pos = index(Line,Assign)) {
  663.         Var = substr(Line,1,Pos-1)
  664.         Val = substr(Line,Pos+AssignLen)
  665.         if (StripWhite) {
  666.         sub("^[ \t]+","",Val)
  667.         sub("[ \t]+$","",Val)
  668.         }
  669.     }
  670.     else {
  671.         Var = Line    # If no value, var is entire line
  672.         Val = ""
  673.     }
  674.     if (!FlagsOK && Val == "") {
  675.         Errs = Errs \
  676.         sprintf("\nBad assignment on line %d of file %s: %s",
  677.         LineNum,File,Line)
  678.         continue
  679.     }
  680.     sub("[ \t]+$","",Var)
  681.     if (Var !~ VarPat) {
  682.         Errs = Errs sprintf("\nBad variable name on line %d of file %s: %s",
  683.         LineNum,File,Var)
  684.         continue
  685.     }
  686.     if (!(Var in Lines)) {
  687.         Lines[Var] = LineNum
  688.         if (Pos)
  689.         Values[Var] = Val
  690.     }
  691.     }
  692.     if (Status)
  693.     Errs = Errs "\nCould not read file " File
  694.     close(File)
  695.     return Errs == "" ? LineNum : substr(Errs,2)    # Skip first newline
  696. }
  697.  
  698. # Variables:
  699. # Data is stored in Options[].
  700. # rcFiles, OptList, VarNames, and EnvSearch are as as described for Opts().
  701. # Global vars:
  702. # Sets OptErr.  Uses ENVIRON[].
  703. # If anything is read from any of the rcfiles, sets READ_RCFILE to 1.
  704. function InitOpts(rcFiles,Options,OptList,VarNames,EnvSearch,
  705. Line,Var,Pos,Vars,Map,CharOpt,NumVars,TypesInd,Types,Type,Ret,i,rcFile,
  706. fNames,numrcFiles,filesRead,Err,Values,retStr) {
  707.     split("",filesRead,"")    # make awk know this is an array
  708.     NumVars = split(VarNames,Vars,",")
  709.     TypesInd = Ret = 0
  710.     if (EnvSearch == -1)
  711.     EnvSearch = NumVars
  712.     for (i = 1; i <= NumVars; i++) {
  713.     Var = Vars[i]
  714.     CharOpt = substr(OptList,++TypesInd,1)
  715.     if (CharOpt ~ "^[:*()#<>&]$")
  716.         CharOpt = substr(OptList,++TypesInd,1)
  717.     Map[Var] = CharOpt
  718.     Types[Var] = Type = substr(OptList,TypesInd+1,1)
  719.     # Do not overwrite entries from environment
  720.     if (i <= EnvSearch && Var in ENVIRON &&
  721.     (Err = AssignVal(CharOpt,ENVIRON[Var],Options,Type,1,Var,0)) < 0)
  722.         return Err
  723.     }
  724.  
  725.     numrcFiles = split(rcFiles,fNames,":")
  726.     for (i = 1; i <= numrcFiles; i++) {
  727.     rcFile = fNames[i]
  728.     if (rcFile ~ "^~/")
  729.         rcFile = ENVIRON["HOME"] substr(rcFile,2)
  730.     else if (rcFile ~ /^\$/) {
  731.         rcFile = substr(rcFile,2)
  732.         match(rcFile,"^[a-zA-Z0-9_]*")
  733.         envvar = substr(rcFile,1,RLENGTH)
  734.         if (envvar in ENVIRON)
  735.         rcFile = ENVIRON[envvar] substr(rcFile,RLENGTH+1)
  736.         else
  737.         continue
  738.     }
  739.     if (rcFile in filesRead)
  740.         continue
  741.     # rcfiles are liable to be given more than once, e.g. UHOME and HOME
  742.     # may be the same
  743.     filesRead[rcFile]
  744.     if ("x" in Options)
  745.         printf "Reading configuration file %s\n",rcFile > "/dev/stderr"
  746.     retStr = ReadConfigFile(Values,Lines,rcFile,"#","=",0,"",1)
  747.     if (retStr > 0)
  748.         READ_RCFILE = 1
  749.     else if (ret != "") {
  750.         OptErr = retStr
  751.         Ret = -1
  752.     }
  753.     for (Var in Lines)
  754.         if (Var in Map) {
  755.         if ((Err = AssignVal(Map[Var],
  756.         Var in Values ? Values[Var] : "",Options,Types[Var],
  757.         Var in Values,Var,0)) < 0)
  758.             return Err
  759.         }
  760.         else {
  761.         OptErr = sprintf(\
  762.         "Unknown var \"%s\" assigned to on line %d\nof file %s",Var,
  763.         Lines[Var],rcFile)
  764.         Ret = -1
  765.         }
  766.     }
  767.  
  768.     if ("x" in Options)
  769.     for (Var in Map)
  770.         if (Map[Var] in Options)
  771.         printf "(%s) %s=%s\n",Map[Var],Var,Options[Map[Var]] > \
  772.         "/dev/stderr"
  773.         else
  774.         printf "(%s) %s not set\n",Map[Var],Var > "/dev/stderr"
  775.     return Ret
  776. }
  777.  
  778. # OptSets is a semicolon-separated list of sets of option sets.
  779. # Within a list of option sets, the option sets are separated by commas.  For
  780. # each set of sets, if any option in one of the sets is in Options[] AND any
  781. # option in one of the other sets is in Options[], an error string is returned.
  782. # If no conflicts are found, nothing is returned.
  783. # Example: if OptSets = "ab,def,g;i,j", an error will be returned due to
  784. # the exclusions presented by the first set of sets (ab,def,g) if:
  785. # (a or b is in Options[]) AND (d, e, or f is in Options[]) OR
  786. # (a or b is in Options[]) AND (g is in Options) OR
  787. # (d, e, or f is in Options[]) AND (g is in Options)
  788. # An error will be returned due to the exclusions presented by the second set
  789. # of sets (i,j) if: (i is in Options[]) AND (j is in Options[]).
  790. #    if ((Err = ExclusiveOptions(OptSets,Options)) != "") {
  791. #    printf "Error: %s\n",Err > "/dev/stderr"
  792. #    Err = 1
  793. #    exit(1)
  794. #    }
  795.  
  796. function ExclusiveOptions(OptSets,Options,
  797. Sets,SetSet,NumSets,Pos1,Pos2,Len,s1,s2,c1,c2,ErrStr,L1,L2,SetSets,NumSetSets,
  798. SetNum,OSetNum) {
  799.     NumSetSets = split(OptSets,SetSets,";")
  800.     # For each set of sets...
  801.     for (SetSet = 1; SetSet <= NumSetSets; SetSet++) {
  802.     # NumSets is the number of sets in this set of sets.
  803.     NumSets = split(SetSets[SetSet],Sets,",")
  804.     # For each set in a set of sets except the last...
  805.     for (SetNum = 1; SetNum < NumSets; SetNum++) {
  806.         s1 = Sets[SetNum]
  807.         L1 = length(s1)
  808.         for (Pos1 = 1; Pos1 <= L1; Pos1++)
  809.         # If any of the options in this set was given, check whether
  810.         # any of the options in the other sets was given.  Only check
  811.         # later sets since earlier sets will have already been checked
  812.         # against this set.
  813.         if ((c1 = substr(s1,Pos1,1)) in Options)
  814.             for (OSetNum = SetNum+1; OSetNum <= NumSets; OSetNum++) {
  815.             s2 = Sets[OSetNum]
  816.             L2 = length(s2)
  817.             for (Pos2 = 1; Pos2 <= L2; Pos2++)
  818.                 if ((c2 = substr(s2,Pos2,1)) in Options)
  819.                 ErrStr = ErrStr "\n"\
  820.                 sprintf("Cannot give both %s and %s options.",
  821.                 c1,c2)
  822.             }
  823.     }
  824.     }
  825.     if (ErrStr != "")
  826.     return substr(ErrStr,2)
  827.     return ""
  828. }
  829.  
  830. ### end of ProcArgs library
  831.  
  832. ### Start of strtol library
  833. # @(#) strtol 1.0 96/03/01
  834. # 96/03/01 john h. dubois iii (john@armory.com)
  835.  
  836. # Convert a value in base Base to an integer.
  837. function strtoi(S,Base,  ret,len,i,conv,digit) {
  838.     if (Base < 2 || Base > 36)
  839.     return ""
  840.     S = tolower(S)
  841.     len = length(S)
  842.     conv = substr("0123456789abcdefghijklmnopqrstuvwxyz",1,Base)
  843.     for (i = 1; i <= len; i++) {
  844.     if (!(digit = index(conv,substr(S,i,1))))
  845.         return ""
  846.     ret = ret * Base + digit - 1
  847.     }
  848.     return ret
  849. }
  850.  
  851. # If Base is 1-36, S is taken to be a number in base Base.
  852. # If Base is 16, an initial 0x or 0X is ignored.
  853. # If Base is 0, an initial 0x or 0X causes Base to be set to 16; otherwise
  854. # Base is set to 10.
  855. # If S is empty or contains any characters not appropriate to a number in
  856. # base Base, a null string is returned.  On success, an integer value is
  857. # returned.
  858. function strtol(S,Base) {
  859.     Base += 0    # yes, this is neccessary
  860.     if (Base < 0 || Base > 36)
  861.     return ""
  862.     if (Base == 0)
  863.     if (S ~ /^0[xX]/) {
  864.         Base = 16
  865.         S = substr(S,3)
  866.     }
  867.     else
  868.         Base = 10
  869.     else if (Base == 16 && S ~ /^0[xX]/)
  870.     S = substr(S,3)
  871.     return strtoi(S,Base)
  872. }
  873.  
  874. # Convert a value in ksh syntax to an integer.
  875. # If s has the form <base>#value, value is taken to be in base <base>.
  876. # Otherwise, it is taken to be a decimal value.
  877. function kshbase(S,  elem) {
  878.     if (split(S,elem,"#") == 2)
  879.     return strtol(elem[2],elem[1])
  880.     else
  881.     return strtol(S)
  882. }
  883. ### End of strtol library
  884. ### start of ntoa lib
  885. # @(#) ntoa 1.0 94/01/01
  886. # Converts integer inval to string representation in base radix & returns it.
  887. # inval is taken to be a signed value; the result is preceded by a minus
  888. # sign if negative.
  889. # If numDig is nonzero, the result (before the minus sign, if any, is added)
  890. # is padded on the left with zeros to make it numDig digits long.
  891. # Then, either the minus sign or (if the result is positive) a space is added.
  892. # This means that the result will always be numDig+1 characters long.
  893. # If the result is longer than numDig before padding, it is left alone.
  894. # If numDig is zero, the leading space is not printed.
  895. # Null is returned on error.
  896. function itoa(inval,radix,numDig,  Buf,value,neg,dig,Sign) {
  897.  
  898.     if (!(2 <= radix && radix <= 36))
  899.     return ""
  900.     if (neg = (inval < 0))
  901.     value = -inval
  902.     else
  903.     value = inval
  904.     if (value == 0)
  905.     Buf = "0"
  906.     while (value > 0) {
  907.     if ((dig = value % radix) > 9)
  908.         # Add digit value to 'a' - 10
  909.         Buf = sprintf("%c",dig + 87) Buf
  910.     else
  911.         # Add digit value to '0'
  912.         Buf = sprintf("%c",dig + 48) Buf
  913.     value = int(value / radix)
  914.     }
  915.     if (neg)
  916.     Sign "-"
  917.     else if (numDig)    # Do this before zeroing numDig
  918.     Sign = " "
  919.     if (numDig)
  920.     for (numDig -= length(Buf); numDig > 0; numDig--)
  921.         Buf = "0" Buf
  922.     return Sign Buf
  923. }
  924.  
  925. # ftoa: Convert a floating-point number to ASCII.
  926. # Converts inval to string representation in base radix & returns it.
  927. # inval is taken to be a signed value; the result is preceded by a minus
  928. # sign if negative.
  929. # fracDig is the number of digits in the output radix to print after the
  930. # decimal point.
  931. # If nDig is nonzero, the result (before the minus sign, if any, is added)
  932. # is padded on the left with zeros to make it nDig digits long.
  933. # Then, either the minus sign or (if the result is positive) a space is added.
  934. # This means that the result will always be nDig+1 characters long.
  935. # If the result is longer than nDig before padding, it is left alone.
  936. # Null is returned on error.
  937. function ftoa(inval,radix,nDig,fracDig,
  938. Buf,value,dig,intPart) {
  939.  
  940.     if (!(2 <= radix && radix <= 36))
  941.     return ""
  942.     intPart = int(inval)
  943.     value = abs(inval - intPart)
  944.     Buf = itoa(int(inval),radix,nDig ? nDig - fracDig - 1 : 0) "."
  945.     for (; fracDig; fracDig--) {
  946.     dig = int(value *= radix)
  947.     # Add digit value to 'a' - 10 or to '0'
  948.     Buf = Buf sprintf("%c",dig + (dig > 9 ? 87 : 48))
  949.     value -= dig
  950.     }
  951.     return Buf
  952. }
  953. ### end of ntoa lib
  954. ### Start of tinfo lib
  955. # @(#) tinfo 1.0 96/11/30
  956. # altInit(): Get alternate character set terminfo capabilities.
  957. # term, noerror: see tiget().
  958. # tinfo: contains the acsc capability, and any of the enacs, smacs, and rmacs
  959. # capabilities that are defined for the terminal.  Each is indexed by its
  960. # capability name.  enacs is used to enable the alternate character set;
  961. # smacs starts it; rmacs ends it.  acsc is the mapping of vt100 alternate
  962. # character codes to those appropriate for the given terminal.
  963. # AltMap is the acsc string broken down with each alternate character indexed
  964. # by its vt100 equivalent.  num is an ordered list of the vt100 characters
  965. # indexed starting with 1, for applications that need to know what order they
  966. # were given in.
  967. # The global _macs[] is set up with _macs[0] = rmacs & _macs[1] = smacs, for
  968. # use by altPrint().
  969. # The alternate characters and their indexes (vt100 equivalents) are:
  970. # 0  solid square block        a  checker board    f  degree symbol
  971. # g  plus/minus            h  board of squares    j  lower right corner
  972. # k  upper right corner        l  upper left corner    m  lower left corner
  973. # n  plus            q  horizontal line    t  left tee
  974. # u  right tee            v  bottom tee        w  top tee
  975. # x  vertical line        +  arrow pointing right    .  arrow pointing down
  976. # -  arrow pointing up        ,  arrow pointing left    `  diamond
  977. # ~  bullet            I  lantern symbol    o  scan line 1
  978. # s  scan line 9
  979. function altInit(tinfo,term,noerror,AltMap,num,  ret,caplist,acsc,len,j,i) {
  980.     if (ret = tiget("acsc",tinfo,term)) {
  981.     # All other types of errors cause tput to print an informative message
  982.     # to stderr, which is not redirected.
  983.     if (!noerror && ret == 1)
  984.         print "Terminal has no acsc capability." > "/dev/stderr"
  985.     return ret
  986.     }
  987.     caplist = "enacs,smacs,rmacs"
  988.     tiget(caplist,tinfo,term)
  989.     acsc = tinfo["acsc"]
  990.     len = length(acsc)
  991.     j = 0
  992.     for (i = 1; i < len; i += 2)
  993.     AltMap[num[++j] = substr(acsc,i,1)] = substr(acsc,i+1,1)
  994.     if ("rmacs" in tinfo)
  995.     _macs[0] = tinfo["rmacs"]
  996.     if ("smacs" in tinfo)
  997.     _macs[1] = tinfo["smacs"]
  998.     return 0
  999. }
  1000.  
  1001. # altPrint: Print characters in either the alternate or standard character set.
  1002. # string is the string to print.
  1003. # alt should be 1 if string is in the alternate character set; 0 if in the
  1004. # standard character set.
  1005. # tinfo contains the smacs and rmacs strings, if needed.
  1006. # altPrint keeps track of whether the terminal is in the standard or alternate
  1007. # character set, and issues smacs and rmacs as needed.
  1008. # It should always be called with alt false at the end of program execution to
  1009. # ensure that the terminal is left in the standard character set.
  1010. # Globals: The character set is tracked in _altPrintSet
  1011. function altPrint(string,alt,tinfo) {
  1012.     if (alt != _altPrintSet) {
  1013.     printf "%s%s",_macs[alt],string
  1014.     _altPrintSet = alt
  1015.     }
  1016.     else
  1017.     printf "%s",string
  1018. }
  1019.  
  1020. # tiget: get terminfo capabilities.
  1021. # capnames is a comma-separated list of terminfo capabilities to get.
  1022. # Each capability is put in tinfo[], indexed by capability name.
  1023. # If term is passed, it is the terminal type to get the capabilities for.
  1024. # If not, the value of the environment variable TERM is used.
  1025. # If noerror is true, error messages are suppressed.
  1026. # Return value: the exit status of the last tput, or -1 if term is not passed
  1027. # and there is no TERM environment variable.
  1028. function tiget(capnames,tinfo,term,noerror,  cmd,RS,ret,names,capname,i) {
  1029.     if (term == "")
  1030.     if ("TERM" in ENVIRON)
  1031.         term = ENVIRON["TERM"]
  1032.     else
  1033.         return -1
  1034.     split(capnames,names,",")
  1035.     RS = ""    # this makes the record separator be "\n\n", which hopefully
  1036.         # is not very common in terminfo capabilities
  1037.     for (i = 1; i in names; i++) {
  1038.     capname = names[i]
  1039.     cmd = "exec tput -T " term " " capname
  1040.     if (noerror)
  1041.         cmd = cmd " 2>/dev/null"
  1042.     cmd | getline
  1043.     if (!(ret = close(cmd)))
  1044.         # printf interprets many of the escape chars in the same manner that
  1045.         # the terminfo library does... not perfect, but better than nothing
  1046.         tinfo[capname] = sprintf($0)
  1047.     }
  1048.     return ret
  1049. }
  1050.  
  1051. function tiget1(capname,term,noerror,  capnames) {
  1052.     delete tinfo[capname]
  1053.     tiget(capname,tinfo,term,noerror)
  1054.     return tinfo[capname]
  1055. }
  1056. ### End of tinfo lib
  1057.